Conversation
Replace closed_loop boolean with a purpose enum (transactional | gift-card | offer) in mint info extensions. Offer mints have NUT-04 minting disabled and keyset-based expiry for promotional ecash distribution. - Add 'offer' to MintPurpose type and account_purpose DB enum - Add expiresAt field to CashuAccount (derived from active keyset final_expiry) - Allow NUT-04 disabled in mint validation for offer mints - Adapt canSendToLightning/canReceiveFromLightning for 3-purpose model - Update isTestMintQueryOptions to handle mints with minting disabled
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Updates to Preview Branch (add-offer-accounts) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
| * Converted from the active keyset's `final_expiry` unix epoch (NUT-02). | ||
| * Null for non-offer accounts or when the keyset has no expiry. | ||
| */ | ||
| expires_at: z.string().nullable().optional(), |
There was a problem hiding this comment.
looking at this plan for adding account state here I'm wondering if expires_at should be a new column, not in the cashu account jsonb.
There was a problem hiding this comment.
I think it should. See my comment here. Do you see some benefits of it being in cashu jsonb?
| * The purpose of a Cashu mint as advertised in its info response. | ||
| * - 'transactional': Regular mint for sending/receiving payments | ||
| * - 'gift-card': Closed-loop mint issuing gift cards | ||
| * - 'offer': Promotional ecash with an expiry |
There was a problem hiding this comment.
nit but should we mention that this one is closed loop too?
| return false; | ||
| } | ||
| const wallet = getCashuWallet(mintUrl); | ||
| const { request: bolt11 } = await wallet.createMintQuoteBolt11(1); |
There was a problem hiding this comment.
should we just remove this check since we discussed how it doesn't work anyway? maybe we should just consider it a test mint if included in knownTestMints
| * Assumes one active keyset per unit on offer mints. | ||
| * @returns The ISO 8601 timestamp when the offer expires, or null if the keyset has no expiry. | ||
| */ | ||
| export const getOfferExpiresAt = ( |
There was a problem hiding this comment.
I would probably have this function return Date | null and then the caller can convert to iso string if needed
Also I think this function should be called getKeysetExpiry or getMintExpiry or something like that since it doesn't do anything purpose specific.
| * plus 'transactional' which also applies to non-cashu account types (e.g. Spark). | ||
| */ | ||
| export type AccountPurpose = 'transactional' | 'gift-card'; | ||
| export type AccountPurpose = 'transactional' | MintPurpose; |
There was a problem hiding this comment.
but transactional already exists on MintPurpose so should be enough to do export type AccountPurpose = MintPurpose
| * Converted from the active keyset's `final_expiry` unix epoch (NUT-02). | ||
| * Null for non-offer accounts. | ||
| */ | ||
| expiresAt: string | null; |
There was a problem hiding this comment.
I wonder if account expiry should be top level account concept and not cashu specific thing. Similar to how purpose is atm even though for Spark it is always transactional atm.
| /** | ||
| * Returns true if the account can send payments through the Lightning network. | ||
| * Returns false for test mints and gift-card accounts. | ||
| * Returns false for offline wallets, test mints, non-transactional accounts, |
There was a problem hiding this comment.
why did we change this to check online status too now? if we are doing that shouldn't we check only status for spark type too?
| * Converted from the active keyset's `final_expiry` unix epoch (NUT-02). | ||
| * Null for non-offer accounts or when the keyset has no expiry. | ||
| */ | ||
| expires_at: z.string().nullable().optional(), |
There was a problem hiding this comment.
I think it should. See my comment here. Do you see some benefits of it being in cashu jsonb?
| currency: Currency, | ||
| ): string | null => { | ||
| const unit = getCashuProtocolUnit(currency); | ||
| const activeKeyset = keysets.find((ks) => ks.unit === unit && ks.active); |
There was a problem hiding this comment.
can there be more than one active keyset for the same unit?
| mintInfo: ExtendedMintInfo, | ||
| ) => { | ||
| return queryOptions({ | ||
| queryKey: ['is-test-mint', mintUrl, mintInfo.isSupported(4).disabled], |
| | 'isOnline' | ||
| >; | ||
| }) { | ||
| const isTestMint = await checkIsTestMint(account.mintUrl); |
There was a problem hiding this comment.
why can't this be a simple fn call anymore?
Replace closed_loop boolean with a purpose enum (transactional | gift-card | offer) in mint info extensions. Offer mints have NUT-04 minting disabled and keyset-based expiry.
I will open follow up PRs that add the account state (active, expired, deleted), and one for the offers UI